package $4reflection;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class $4_2_FnGetField {
    static final Logger logger = LoggerFactory.getLogger($4_2_FnGetField.class);
    public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
        /**
         * 访问字段
         * 对任意的一个Object实例，只要我们获取了它的Class，就可以获取它的一切信息。
         * Class类提供了以下几个方法来获取字段：
         * Field getField(name)：根据字段名获取某个public的field（包括父类）
         * Field getDeclaredField(name)：根据字段名获取当前类的某个field（不包括父类）
         * Field[] getFields()：获取所有public的field（包括父类）
         * Field[] getDeclaredFields()：获取当前类的所有field（不包括父类）
         * 
         * 获取字段值
         * 利用反射拿到字段的一个Field实例只是第一步，我们还可以拿到一个实例对应的该字段的值。
         * 
         */
        
        Class stdClass = Student.class;
        // 获取public字段"score":
        System.out.println(stdClass.getField("score"));
        // 获取继承的public字段"name":
        System.out.println(stdClass.getField("name"));
        // 获取private字段"grade":
        System.out.println(stdClass.getDeclaredField("grade"));
        
        Field f = String.class.getDeclaredField("value");
        logger.info("f.getName():{}",f.getName()); // "value"
        logger.info("f.getType():{}",f.getType()); // class [B 表示byte[]类型
        int m = f.getModifiers();
        logger.info("Modifier.isFinal(m)    :{}",Modifier.isFinal(m)); // true
        logger.info("Modifier.isPublic(m)   :{}",Modifier.isPublic(m)); // false
        logger.info("Modifier.isProtected(m):{}",Modifier.isProtected(m)); // false
        logger.info("Modifier.isPrivate(m)  :{}",Modifier.isPrivate(m)); // true
        logger.info("Modifier.isStatic(m)   :{}",Modifier.isStatic(m)); // false
        
        /**
         * 获取字段值
         * 利用反射拿到字段的一个Field实例只是第一步，我们还可以拿到一个实例对应的该字段的值。
         * 
         */
        Object p = new Person("Xiao Ming");
        Class c = p.getClass();
        Field fname = c.getDeclaredField("name");
        fname.setAccessible(true);
        Object value = fname.get(p);
        System.out.println(value); // "Xiao Ming"
        
        /**
         * 设置字段值
         * 通过Field实例既然可以获取到指定实例的字段值，自然也可以设置字段的值。
         * 设置字段值是通过Field.set(Object, Object)实现的，其中第一个Object参数是指定的实例，第二个Object参数是待修改的值。
         */
        fname.set(p, "Xiao Hong");
        logger.info("p.getName():{}",fname.get(p));
        
        /**
         * 小结
         * Java的反射API提供的Field类封装了字段的所有信息：
         * 通过Class实例的方法可以获取Field实例：getField()，getFields()，getDeclaredField()，getDeclaredFields()；
         * 通过Field实例可以获取字段信息：getName()，getType()，getModifiers()；
         * 通过Field实例可以读取或设置某个对象的字段，如果存在访问限制，要首先调用setAccessible(true)来访问非public字段。
         * 通过反射读写字段是一种非常规方法，它会破坏对象的封装。
         */
    }
}
